using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Office.Server;
using Microsoft.Office.Server.Administration;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.SharePoint;
using System.Web;
using System.Xml;
using Winsmarts.PPI.Common;

using System.Reflection;

namespace ProfilePropertyMgr
{
    internal static class WorkerBee
    {
        internal static void ExportProfiles(ProgramInputs inputs, UserProfileManager profileManager)
        {
            // Variable Naming convention: <objectinConcern><Property/Properties>

            PropertyCollection profileProperties = profileManager.Properties;

            XmlDocument exportProperties = new XmlDocument();
            XmlNode nodeProperties = exportProperties.CreateNode(XmlNodeType.Element, "Properties", "");
            exportProperties.AppendChild(nodeProperties);

            PropertyInfo[] allProps = typeof(Property).GetProperties();
            Dictionary<string, PropertyInfo> typeProperties = new Dictionary<string, PropertyInfo>();
            foreach (PropertyInfo typeProperty in allProps)
            {
                typeProperties.Add(typeProperty.Name, typeProperty);
            }

            foreach (Property profileProperty in profileProperties)
            {
                // Should I be even exporting this property?
                bool isExportable = ShouldExport(typeProperties["Name"], profileProperty, true);

                if (isExportable)
                {
                    XmlNode nodeProperty = exportProperties.CreateNode(XmlNodeType.Element, "Property", "");
                    nodeProperties.AppendChild(nodeProperty);

                    foreach (PropertyInfo typeProperty in typeProperties.Values)
                    {
                        if (ShouldExport(typeProperty, profileProperty, false))
                        {
                            XmlNode nodePropertyInfo = exportProperties.CreateNode(XmlNodeType.Element, "PropertyInfo", "");

                            XmlAttribute attribProfilePropInfo;
                            object reflectedValue;

                            //Name
                            attribProfilePropInfo = exportProperties.CreateNode(XmlNodeType.Attribute, "Name", "") as XmlAttribute;
                            attribProfilePropInfo.Value = typeProperty.Name;
                            nodePropertyInfo.Attributes.Append(attribProfilePropInfo);

                            // Data Type
                            attribProfilePropInfo = exportProperties.CreateNode(XmlNodeType.Attribute, "Type", "") as XmlAttribute;
                            attribProfilePropInfo.Value = typeProperty.PropertyType.AssemblyQualifiedName;
                            nodePropertyInfo.Attributes.Append(attribProfilePropInfo);

                            // Value
                            attribProfilePropInfo = exportProperties.CreateNode(XmlNodeType.Attribute, "Data", "") as XmlAttribute;
                            reflectedValue = typeProperty.GetValue(profileProperty, null);
                            attribProfilePropInfo.Value = (reflectedValue == null) ? String.Empty : reflectedValue.ToString();
                            nodePropertyInfo.Attributes.Append(attribProfilePropInfo);

                            nodeProperty.AppendChild(nodePropertyInfo);
                        }
                    }

                    // lets see if this prop has values
                    if (profileProperty.ChoiceType == ChoiceTypes.Closed || profileProperty.ChoiceType == ChoiceTypes.Open)
                    {
                        // double check choice list
                        if (profileProperty.ChoiceList != null)
                        {
                            XmlElement oChoicesNode = exportProperties.CreateElement("Choices");
                            nodeProperty.AppendChild(oChoicesNode);

                            foreach (string sChoice in profileProperty.ChoiceList.GetAllTerms(false))
                            {
                                XmlElement oChoiceNode = exportProperties.CreateElement("Choice");
                                oChoicesNode.AppendChild(oChoiceNode);

                                oChoiceNode.InnerText = sChoice;
                            }
                        }
                    }
                }
            }

            exportProperties.Save(inputs.FileName);
        }

        internal static void ImportProfiles(ProgramInputs inputs, UserProfileManager profileManager)
        {
            XmlDocument docImportProperties = new XmlDocument();
            docImportProperties.Load(inputs.FileName);

            PropertyInfo[] typeProperties = typeof(Property).GetProperties();
            //Create an enumerable dictionary of this - will help afterwards.
            Dictionary<string, PropertyInfo> reflectedProperties = new Dictionary<string, PropertyInfo>();
            foreach (PropertyInfo typeProperty in typeProperties)
            {
                reflectedProperties.Add(typeProperty.Name, typeProperty);
            }
            PropertyCollection pc = profileManager.Properties;

            XmlNodeList importProperties = docImportProperties.SelectNodes("./Properties/Property");

            foreach (XmlNode importProperty in importProperties)
            {
                Property p = pc.Create(false);
                string currPropName = importProperty.SelectSingleNode("./PropertyInfo[@Name='Name']").Attributes["Data"].Value;
                foreach (XmlNode propInfoNode in importProperty.SelectNodes("./PropertyInfo")) // modified - make sure only picks up PropertyInfo nodes and not Choices node (if present)
                {
                    PropertyInfo relevantPropertyInfo = reflectedProperties[propInfoNode.Attributes["Name"].Value];
                    Type propType = Type.GetType(propInfoNode.Attributes["Type"].Value);
                    // object importPropertyValue = Activator.CreateInstance(propType);
                    object importPropertyValue = null;
                    if (propType.IsEnum)
                    {
                        importPropertyValue = Enum.Parse(propType, propInfoNode.Attributes["Data"].Value);
                    }
                    else
                    {
                        importPropertyValue = Convert.ChangeType(propInfoNode.Attributes["Data"].Value, propType);
                    }
                    relevantPropertyInfo.SetValue(p, importPropertyValue, null);
                }

                // ok, we're done with the properties, now check to see if we need multi-choice

                // lets see if this prop has values
                if (p.ChoiceType == ChoiceTypes.Closed || p.ChoiceType == ChoiceTypes.Open)
                {
                    // now lets check to see if we have nodes for it

                    XmlNode oChoicesNode = importProperty.SelectSingleNode("Choices");

                    if (oChoicesNode != null)
                    {
                        foreach (XmlNode oChoiceNode in oChoicesNode.ChildNodes)
                        {
                            p.ChoiceList.Add(oChoiceNode.InnerText);
                        }
                    }
                }

                // end of edit

                try
                {
                    pc.Add(p);
                }
                catch (DuplicateEntryException)
                {
                    Trace.WriteLine("Ignoring Duplicate Entry:" + currPropName);
                }
            }
        }

        // In English, the below method tells me to export only properties that are both read/write,
        // That are valuetypes or string, exclude UserProfile_GUID and whatever starts with SPS-. 
        // SPS- is reserved for sharepoint, and UserProfile_GUID cannot be deleted/added.
        private static bool ShouldExport(PropertyInfo typeProperty, Property profileProperty, bool entireProperty)
        {
            bool toReturn = false;

            toReturn = typeProperty.CanRead & typeProperty.CanWrite &
                        (
                            typeProperty.PropertyType.IsValueType |
                            typeProperty.PropertyType.FullName.Equals("System.String")
                        );

            // Proceed only if toReturn is still true ;
            // Also, the following applies to only entire properties
            if (toReturn & entireProperty)
            {
                object reflectedValue = typeProperty.GetValue(profileProperty, null);
                string strReflectedValue = (reflectedValue == null) ? String.Empty : reflectedValue.ToString();
                toReturn &= !strReflectedValue.Equals("UserProfile_GUID");
                toReturn &= !strReflectedValue.StartsWith("SPS-");
            }
            return toReturn;
        }
    }
}
